﻿/* 
 Copyright (C) 2011  Amblox Project

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

using System;
using System.Net.Sockets;

using Amblox.Encoding;
using Amblox.Game;
using System.Threading;

namespace Amblox.Net
{
    public class NetConnection
    {
        public int ID;
        Socket Connection;
        byte[] DataBuffer;
        HabboClient Client;
        bool DestroyInit = false;

        DateTime PongedTime;

        public int Latency = 0;

        public bool PongStatus;

        public string IP
        {
            get
            {
                try
                {
                    return Connection.RemoteEndPoint.ToString().Split(':')[0];
                }
                catch
                {
                    return "0.0.0.0";
                }
            }
        }

        public bool Destroyed
        {
            get
            {
                if (Connection == null && DataBuffer == null && Client == null && ID == 0)
                    return true;
                else
                    return false;
            }
        }

        public NetConnection(int ID, Socket Con)
        {
            this.ID = ID;
            Connection = Con;
            DataBuffer = new byte[1024];

            try
            {
                Connection.BeginReceive(DataBuffer, 0, DataBuffer.Length, SocketFlags.None, new AsyncCallback(OnReceive), null);
            }
            catch (Exception e)
            {
                Log.WriteError("Unhandled error on setup connection: " + e.Message);
                Log.WriteError("Connection Aborted");

                Destroy();
            }
        }

        public void Destroy()
        {
            if (Destroyed)
                return;

            DestroyInit = true;
            Thread.Sleep(100); // Crea un ritardo di 100ms per consentire a tutti i void di finire il lavoro.

            try
            {
                Connection.Close();
            }
            catch { }

            Connection = null;
            DataBuffer = null;

            NetListener.RemoveConnection(ID);

            ID = 0;

            try
            {
                Client.Connection = null;
                Client.Destroy();
            }
            catch { }

            Client = null;
        }

        public void SendData(byte[] Data)
        {
            Connection.Send(System.Text.Encoding.Default.GetBytes(System.Text.Encoding.Default.GetString(Data) + Convert.ToChar(1)));
        }

        public void SendData(string Data)
        {
            SendData(System.Text.Encoding.Default.GetBytes(Data));
        }

        public void SendData(FuseBuilder Data)
        {
            SendData(Data.ToString());
        }

        public void OnReceive(IAsyncResult IAs)
        {
            if (DestroyInit || Connection == null || Destroyed)
                return;

            try
            {
                int Length = Connection.EndReceive(IAs);
                string Data = System.Text.Encoding.Default.GetString(DataBuffer, 0, Length);

                if (Data == "")
                    goto Setup;

                try
                {
                    if (Data.Substring(0, "<policy-file-request/>".Length) == "<policy-file-request/>")
                    {
                        SendData("<?xml version=\"1.0\"?>\r\n<!DOCTYPE cross-domain-policy SYSTEM \"/xml/dtds/cross-domain-policy.dtd\">\r\n<cross-domain-policy>\r\n<allow-access-from domain=\"*\" to-ports=\"1-31111\" />\r\n</cross-domain-policy>\x0");
                        Destroy();
                        return;
                    }
                }
                catch { }

                while (Data != "")
                {
                    try
                    {
                        if (Data.Length < 5)
                            Destroy();

                        int length = JeaxEncoding.decodeB64(Data.Substring(0, 3));
                        string data = Data.Substring(3, length);

                        if (data.Length < 2)
                            Destroy();

                        try
                        {
                            if (data.Substring(0, 2) == "CN")
                                Client = new HabboClient(this);

                            if (Client == null)
                                Destroy();

                            Client.Handler.ElaboratePacket(data);
                        }
                        catch (Exception e)
                        {
                            Log.WriteError("Unhandled error on elaborating received data: " + e.Message);
                            Log.WriteError("Connection Aborted");

                            Destroy();
                        }

                        Data = Data.Substring(3 + length);
                    }
                    catch
                    {
                        Destroy();
                    }
                }

            Setup:
                if (!Destroyed)
                    Connection.BeginReceive(DataBuffer, 0, DataBuffer.Length, SocketFlags.None, new AsyncCallback(OnReceive), null);
            }
            catch
            {
                Destroy();
            }
        }

        public void Ping()
        {
            SendData(new FuseBuilder(50));
            PongStatus = false;
            PongedTime = DateTime.Now;
        }

        public void Pong()
        {
            PongStatus = true;
            Latency = DateTime.Now.Subtract(PongedTime).Milliseconds;
        }
    }
}
